using System;
using System.Collections.Generic;
using System.Text;
using Zsa.Emulation.Memory;

namespace Zsa.Emulation.Input
{

    /// <summary>
    /// Managed input devices and maps them to main memory.
    /// </summary>
    public class ControllerIO : IMemoryStream
    {
        #region Fields
        /// <summary>
        /// The first input register.
        /// </summary>
        private InputRegister _RegisterOne;

        /// <summary>
        /// The player two input register.
        /// </summary>
        private InputRegister _RegisterTwo;

        /// <summary>
        /// True if a byte of 1 has been written to register and 
        /// awaiting 0 to reset the register.
        /// </summary>
        private bool _InStrobe;
        #endregion Fields

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="T:ControllerIO"/> class.
        /// </summary>
        public ControllerIO()
        {
            _RegisterOne = new InputRegister(NesInputDeviceType.PortOne);
            _RegisterTwo = new InputRegister(NesInputDeviceType.PortTwo);
        }
        #endregion Constructor

        #region Methods


        /// <summary>
        /// Reads the specified address.
        /// </summary>
        /// <param name="address">The address.</param>
        /// <returns></returns>
        public byte Read(int address)
        {
            switch (address)
            {
                case 0:
                case 0x4016:
                    return (byte)(_RegisterOne.Read()); 
                case 1:
                case 0x4017:
                    //return 0;
                   return (byte)(_RegisterTwo.Read()); 
            }
            throw new ArgumentOutOfRangeException("address");
        }

        /// <summary>
        /// Reads the specified buffer.
        /// </summary>
        /// <param name="buffer">The buffer.</param>
        /// <param name="address">The address.</param>
        /// <param name="count">The count.</param>
        public void Read(byte[] buffer, int address, int count)
        {
            throw new NotSupportedException();
        }



        /// <summary>
        /// Writes the specified buffer.
        /// </summary>
        /// <param name="buffer">The buffer.</param>
        /// <param name="address">The address.</param>
        /// <param name="count">The count.</param>
        public void Write(byte[] buffer, int address, int count)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Writes the specified address.
        /// </summary>
        /// <param name="address">The address.</param>
        /// <param name="romByte">The rom byte.</param>
        public void Write(int address, byte data)
        {
            //if (address == 0x10)
            //_RegisterOne.Write(data);
            if (_InStrobe
                && (data & 1) == 0) // Should we make sure the data being written is zero?
            {

                _RegisterOne.ReadCount = 0;
                _RegisterTwo.ReadCount = 0;
                _InStrobe = false;
            }
            else if ((data & 1) == 1 && !_InStrobe)
            {
                _InStrobe = true;
            }
            else
            {
                _RegisterOne.ReadCount = 0;
                _RegisterTwo.ReadCount = 0;

                _InStrobe = false;
            }
        }

        /// <summary>
        /// Reads the specified address.
        /// </summary>
        /// <param name="address">The address.</param>
        /// <returns></returns>
        public byte Read(ushort address)
        {
            if (address == 0)
            {
                return _RegisterOne.Read();
            }
            else if (address == 1)
            {
                return _RegisterTwo.Read();
            }
            else
            {
                throw new ArgumentOutOfRangeException();
            }
        }

        /// <summary>
        /// Reads the specified address.
        /// </summary>
        /// <param name="address">The address.</param>
        /// <returns></returns>
        public ushort ReadUInt16(int address)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Reads the specified address.
        /// </summary>
        /// <param name="address">The address.</param>
        /// <returns></returns>
        public ushort ReadUInt16(ushort address)
        {
            throw new NotSupportedException();
        }




        #endregion Methods

        #region Properties
        /// <summary>
        /// Gets the length of the stream.
        /// </summary>
        /// <value>The size.</value>
        public int Length
        {
            get { return 2; }
        }

        /// <summary>
        /// Gets the port one register. 
        /// </summary>
        /// <value>Used to set the values read from 0x4016.</value>
        internal InputRegister PortOne
        {
            get
            {
                return _RegisterOne;
            }
        }

        /// <summary>
        /// Gets the port two register. 
        /// </summary>
        /// <value>Used to set the values read from 0x4017</value>
        internal InputRegister PortTwo
        {
            get
            {
                return _RegisterTwo;
            }
        }
        #endregion Properties
    }
}
